package org.NooLab.browserapp;

import java.util.Map;


import org.NooLab.browserapp.app.BrousProperties;
import org.NooLab.browserapp.app.DataStreamProviderIntf;
import org.NooLab.browserapp.app.PersistenceSettings;
import org.NooLab.browserapp.app.up.IniProperties;
import org.NooLab.browserapp.app.up.NNBStartup;
import org.NooLab.browserapp.components.BrowserFactory;
import org.NooLab.structures.AppInstanceAbstract;
import org.NooLab.structures.ArgumentsCliReaderAbstract;
import org.NooLab.structures.ArgumentsIntf;
import org.NooLab.structures.ArgumentsPublicIntf;
import org.NooLab.structures.InstanceProcessControlIntf;
import org.NooLab.utilities.datatypes.ValuePair;
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.files.PathFinder;
import org.NooLab.utilities.logging.LogControl;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.resources.ResourceLoader;






/**
 * 
 * this application is a frameless app and wraps the NooLabNativeBrowser, such that 
 * this app could be run a as a process via ProcessBuilder.
 * 
 * This app provides a small infrastructure to communicate via TCP.
 * For hat purpose it accepts commandline parameters
 * 
 * 
 * 
 * 
 *
 */
public class NooLabNativeBrowserApp {


	private App nnbapp;
	
	//=========================================================================
	public static void main(String[] args) {

		try {
			
			System.out.println("Starting NNB-application...");
			
			new NooLabNativeBrowserApp(args);
				
			System.out.println("Started.");
			PrintLog.Delay(15000);
			
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		


	}


	
	//=========================================================================

	
	protected NooLabNativeBrowserApp(String[] args){
		
		try {
			
			NNBStartup.setApplicationID("nnbapp", this.getClass());
			System.out.print("-1-");
			Arguments a = new Arguments();
			System.out.print("-2-");
			ArgumentsPublicIntf arguments = a.parse(args);
			System.out.print("-3-");
			(nnbapp = new App( 0, arguments, this.getClass())).go();
			System.out.println("-4-");
		} catch (Exception e) {
			e.printStackTrace();
		}
		 
	}


	public void close() {
		nnbapp.close();
		
	}
	
	
}	
	@SuppressWarnings("rawtypes")
	class Arguments extends ArgumentsCliReaderAbstract implements ArgumentsIntf {
		
		public final String _APP_COMPLETION_EXIT    = InstanceProcessControlIntf._APP_COMPLETION_EXIT ;
		public final String _APP_COMPLETION_SERVICE = InstanceProcessControlIntf._APP_COMPLETION_SERVICE ;
		
		public final String  _APP_CONTEXT_ITEXX = InstanceProcessControlIntf._APP_CONTEXT_ITEXX ;
		public final String  _APP_CONTEXT_ALONE = InstanceProcessControlIntf._APP_CONTEXT_ALONE ;
		

		String sourceFileDedicated="",sourceFilesByFolder="",sourceFileByListingFile="";
		
		protected ArgumentsPublicIntf arguments ;
		public Map argumentsMap;
		
		
		
		// ------------------------------------------------------------------------
		@SuppressWarnings("unchecked")
		public Arguments(){
			super();
			setParent( this ) ;
			
			//default = standalone
			contextMode = InstanceProcessControlIntf._APP_CONTEXT_ALONE ; // itexx or standalone, running user based projects	
		}
		// ------------------------------------------------------------------------

		// preparation of arguments and stable parts of the set of arguments 
		// is outsourced to the abstract class
		// else the basic parts of the check are presented in a more convenient way
		
		@SuppressWarnings("unchecked")
		@Override
		public ArgumentsPublicIntf parse( String[] _args ){
			
			String avStr, str;
			
			
			// jarDir = fileutil.getUserDir();
			str = fileutil.getUserHomeDir() ;
			str = IniProperties.fluidSomProjectBasePath; // before, we passed readbinpref() !
			homeDir = DFutils.createPath( str, "app/ling/") ;
			
			LogControl.Level=2;
			
			defineArguments( new String[]{ "prj","homedir","cfg","sourcemode",
					                       "onCompletion","name","comm",
					                       "context","flavor",
					                       "loglevel"});
			
			try {
				String filename="";
				
				if ((_args==null) || (_args.length==0)){
					filename = "NooLabNLP.clia";
					
					if (str.indexOf("/bin")>0){
						filename = DFutils.createPath(str , filename);
					}else{
						filename = DFutils.createPath(str , "bin/"+filename);
					}

					if (DFutils.fileExists(filename)){
						_args = strgutil.changeArrayStyle( (fileutil.readFileintoVectorstringTable(filename)) );
						
					}
				}

	PrintLog.Print(1, "path for clia : "+filename);		
	PrintLog.Print(1, "received args : "+ strgutil.arr2text(_args, "", "  "));

				arguments = prepareArguments( homeDir, _args ) ;

			} catch (Exception e) {
				showArguments( getDescription() );
				System.exit(91);			
			}  
			
			argumentsMap = getArgumentsMap();
			
			
			if (handlingTreatment( new String[]{"flavor"})){

				String fstr = (String)getMappedTopic( new String[]{"flavor"} );
				ValuePair bagField = new ValuePair("flavor",fstr);
				fieldBag.add(bagField) ; 
				
				// this.setFlavor(flavor = fstr );
				
				
			}
			if (handlingTreatment( new String[]{"prj","proj","project"})){
			
				projectName = (String)getMappedTopic( new String[]{"prj","proj","project"} );
				
				
				NNBStartup.setLastProjectName(projectName);
				IniProperties.lastProjectName = projectName;
				
				str = IniProperties.getFluidSomProjectBasePath() ;
				if (IniProperties.getsBinPath().length()==0){
					
					str = (new PathFinder()).getAppBinPath(this.getClass(), false);
					IniProperties.setBinPath(str) ;
				}
				
				IniProperties.saveIniProperties();
				str = IniProperties.getFluidSomProjectBasePath() ;
			}
			
			int rn = remainingTopics.size();
			
			// the rest
			if (rn>0){
				for (int i=0;i<rn;i++){
					String topic = (String) remainingTopics.get(i); 
					avStr = getArgumentValueStr( new String[]{topic} );
					if (avStr.length()>0){
						
						if (topic.toLowerCase().contentEquals("startsomclients")){
							// -startSomClients:1,7211,7210
							String[] scs = avStr.split(",") ;
							if (scs.length==3){
								if (strgutil.isNumericX(scs[0])){
									startSomClients = strgutil.getNumFromStr(scs[0], 0);
								}
								int portnum = 1;
								if (strgutil.isNumericX(scs[1])){
									portnum = strgutil.getNumFromStr(scs[1], 0);
									if (portnum>1090){
										inPort = portnum;  
									}
								}  
								if (strgutil.isNumericX(scs[2])){
									portnum = strgutil.getNumFromStr(scs[2], 0);
									if (portnum>1090){
										outPort = portnum;
									}
									
								}
							}
						}
					}// sth found
				}//i->
			}
			
			if (_args.length==0){
				showArguments( getDescription() );
				System.exit(12);
			}
			
			if (defaultsRequested){
				
				homeDir     = fileutil.createpath( fileutil.getUserDir(), "ling") ;
				projectName = "itexx" ;
				configFile  = fileutil.createpath( homeDir, "nlp-config.xml") ;
				// this file will be read by DocoControlProperties.readSettings()
			}
			
					
					
			setReference( (Object)this);
			return (ArgumentsPublicIntf) this;
		}
		

		
		/*
			-prj:"test2"
			-homedir:"D:/data/iTexx/DocumentServer"
			-cfg:"D:/data/iTexx/DocumentServer/test1/config/docoserv-config.xml"
		 */

		
		

		public boolean startApplet() {
			return appletRequested;
		}

		
		@Override
		public String get_APP_COMPLETION_EXIT() {
			return _APP_COMPLETION_EXIT;
		}

		@Override
		public String get_APP_COMPLETION_SERVICE() {
			return _APP_COMPLETION_SERVICE;
		}

		@Override
		public String get_APP_CONTEXT_ITEXX() {
			return _APP_CONTEXT_ITEXX;
		}

		@Override
		public String get_APP_CONTEXT_ALONE() {
			return _APP_CONTEXT_ALONE;
		}

		public ArgumentsPublicIntf getArguments() {
			return arguments;
		}

		@Override
		public String[] getDescription() {
			String[] dParts = new String[3];
			 
			
			dParts[0] = "Values (examples) appear subsequent to the : \n"+
			        	"-prj:\"test2\"  \n"+
			        	"-homedir:\"D:/data/iTexx/app/ling\" \n"+
			        	" \n" ;

			dParts[1] = "Default values are activated by parameter \"-default\" or \"-d\" and are set to: \n"+
				        "-prj: docoserv  \n"+
						"-homedir: <path of jar>/DocumentServer \n"+
						"\n";
			
			
			dParts[2] = "Additional Information: \n"+
			      "- The database file will be written to the folder \"<homedir>/<project name>/storage\".  \n"+
			      "- If no configuration file has been provided, a default file will be created into \n" +
			      "  the folder \"<homedir>/<project name>/config\". \n"+
			      "  Note that you then have to edit the configuration file, namely the section defining the \n"+
			      "  the folders to be observed.\n"+
			      "- You may stop the application by creating a file \"stop\" within the folder containing \n"+
			      "  the configuration file.  \n"+
			      "- The parameters for activating the notification are port numbers, (1) in, (2) out, where \n"+
			      "  the in-port allows for some udimaentary control, such like stopping the application, or \n"+
			      "  re-reading the configuration. \n"+
			      "- For starting the interactive applet version (e.g. for testing) use the \n"+
			      "  parameter \"-a\" or \"-applet\" \n\n"+" ";
			
			
			return dParts;
		}

		@Override
		public int[] getPorts() {
			return ports;
		}

		@Override
		public int[] getServicePorts() {		
			return servicePorts ;
		}

		@Override
		public String getAppSecret() {
			return appsecret;
		}
		
	}


// ----------------------------------------------------------------------------




@SuppressWarnings("rawtypes")
class App{
	
	public String processName;
	public String secret;

	
	BrowserAppInstance brousInstance;
	 
	ArgumentsPublicIntf arguments;
	
	PrintLog out ;
	DFutils fileutil = new DFutils();
	boolean instanceStopped;

	// ========================================================================
	
	public App( int as_service, ArgumentsPublicIntf args, Class pubClass) {	
	
		String fname = "" ; 
		
		arguments = args;
		
		// use LogControl to control output of log messages
		LogControl.globeScope = 2; // 1=local definitions  default = 2
		LogControl.Level = 2 ;     // the larger the more detailed logging... (could be really verbose!)

		out = new PrintLog(2, true) ;
		out.setPrefix("[nooNLP]") ;
		
		
		try {
			String lastCfgFile ="" ;
			
			NNBStartup.setProjectSpaceLabel( arguments.getProjectName()) ;
			NNBStartup.setLastProjectName(arguments.getProjectName());
			
			IniProperties.loadIniFile() ;
			
			lastCfgFile = NNBStartup.getLastDataSet();
			if ((lastCfgFile==null) || (lastCfgFile.trim().length()<=1)){
				if (NNBStartup.getApplicationID().length()==0 ){
					NNBStartup.setApplicationID( this.getClass().getName().toLowerCase(),pubClass ) ;
				}
				
			}
			IniProperties.saveIniProperties() ;
			
			if (DFutils.fileExists(arguments.getConfigFile())==false){
				if ((arguments.getConfigFile().length()>0)){

					
					// lastCfgFile = IniProperties.dataSource ;
					if (arguments.getProjectName().length()==0){
						arguments.setProjectName ( "default") ;
					}
					
					fname = DFutils.createPath( arguments.getHomeDir(), arguments.getProjectName()+"/config/") ;
					fname = DFutils.createPath( fname, arguments.getConfigFile()) ;
					if (DFutils.fileExists(fname)){
						arguments.setConfigFile (fname);
					}else{
						if (fileutil.filenameIsUsable(fname)){
							arguments.setConfigFile (fname);
						}
					}
				}
				
				
				
				if (DFutils.fileExists(arguments.getConfigFile())==false){

					// this takes the internal resources and creates an external file from it
					
					createDefaultConfig();

					// we need not to stop in the iTexx context
					if (arguments.getContextMode().contentEquals(InstanceProcessControlIntf._APP_CONTEXT_ITEXX)==false){
						String msgstr = "you now have to edit the configuration file : \n" +
						                 "  " + arguments.getConfigFile();
						NNBStartup.showMsg(msgstr);

						// DocoServStartup.introduceDataSet(
						// arguments.configFile ) ;

						IniProperties.saveIniProperties();
						// copy the filename to the system clipboard
						System.exit(11);
					}

				}else{
					
				}
				
			}
			
			IniProperties.saveIniProperties() ;
			
			_showCurrentInputSettings();
			
			
			fname=fname+" " ;
			
		} catch (Exception e) {
			e.printStackTrace();
		}

		
		
	}
	

	public void close() {
		// TODO Auto-generated method stub
		brousInstance.close(1) ;
	}


	private void createDefaultConfig() throws Exception {
		 
		String filename = "", filstr,resourcePath;
		boolean rB;

		resourcePath = "org/NooLab/openNLP/app/resources/nnb-config-xml" ;
		ResourceLoader rsrcLoader = new ResourceLoader();   
		
		rB = rsrcLoader.loadTextResource( this.getClass(), resourcePath  ) ;
		if (rB){  
			filstr = rsrcLoader.getTextResource();
			
			filename = arguments.getConfigFile() ;
			
			fileutil.manageBakFile(filename, 10);
			fileutil.writeFileSimple( filename , filstr);
			
		}else{
			throw(new Exception("unable to load resources (create-db-sql-xml) or \n"+
					            "to create a new configuration file : "+filename)) ;
		}


		
	}


	
	private void _showCurrentInputSettings(){
		String  pathitx="";
		
		if (arguments.getContextMode().contentEquals(InstanceProcessControlIntf._APP_CONTEXT_ITEXX) ){
			pathitx = "app/ling";
		}else{

			if (NNBStartup.isConfigAvailable(pathitx) == false) {

			}
		}
		out.print(1,"current project : "+ NNBStartup.getLastProjectName());
		 
	}


	
	public void go() {


		instanceStopped = false ;
		
		startEngines();
		
		PrintLog.Delay(2000) ;
		// we stop from the outside by writing a file "stop" to the config directory
		while ( instanceStopped == false){
			PrintLog.Delay(100) ;
		}
		brousInstance.close(0) ;

	}

	
	/**
	 * 
	 * @param somtype 0=default: simple, single run; 1=som optimizer
	 * @param lastProjectName2 
	 */
	private void startEngines(){
		
		if (brousInstance!=null){
			return;
		}

		out.print(1,"starting project <"+ IniProperties.lastProjectName+"> ...");
		
		// setting worktypes
		
		
		brousInstance = new BrowserAppInstance( this );
		
		if (arguments!=null){
			brousInstance.contextMode = arguments.getContextMode() ;
			
			brousInstance.homedir      = (String)arguments.getMappedTopic("homeDir");
			brousInstance.projectname  = (String)arguments.getMappedTopic( new String[]{"project","prj","proj"});

			brousInstance.logLevel     = (Integer)arguments.getMappedTopic("loglevel");
			
			brousInstance.onCompletion = (String)arguments.getMappedTopic("oncompletion"); 
						  
			brousInstance.flavor       = (String)arguments.getMappedTopic("flavor");
			
			
			
			brousInstance.appSecret    = (String)arguments.getMappedTopic("appsecret");
			
			brousInstance.processName  = (String)arguments.getMappedTopic("name");
			
			brousInstance.setArguments( arguments.getArgumentsObj());
			// this needs a simple tcp box...
		}
		
		boolean hb = brousInstance.checkForConfiguration();
		
		if (hb==false){
			brousInstance.close(-5);
		}
						int oport = arguments.getOutPort();
						int iport = arguments.getInPort();

		// is located in : org.NooLab.structures	
		brousInstance.prepareTcpBox(oport,iport);  // must be before the wait object !
		
		if ((brousInstance.contextMode.toLowerCase().contentEquals("itexx")) &&
			(brousInstance.waitfor>=1)){
			// waitfor-secret for testing is "adkhf-23487asdf-3487sdfr-324"
			out.print(2, "Application will wait a few seconds for final clearance...");
			brousInstance.waitfor();
		}
		
		
		// instance will check whether it could resume...
		brousInstance.startInstance() ;
		
		while (brousInstance.isRunning()){
			PrintLog.Delay(500);
		}
	}

	
}




@SuppressWarnings("rawtypes")
/*
-homedir:"D:/data/iTexx/app/ling" 
-context:itexx
-comm: in-7205,out-7206
-name:itexxwin
-loglevel:2




 */




class BrowserAppInstance extends AppInstanceAbstract{
  
	
	public String flavor;
	BrowserFactory nlpFactory;
	BrousProperties brProperties;
	
	
	int instanceType;
	int nodeCount = 400;
	
	App app;
	private String nlpFlavor ="";
	private boolean isRunning;
	private int aliveCheckc=0;
		
	
	// ========================================================================
	public BrowserAppInstance(App app){
		
		// these two string values are just for convenience of console output, 
		// it is not used by the application itself for any control
		super( "NlpApp", "");
		
		processName = "nlp";
		
		this.app = app;
		
		instance = this;
		out = app.out ;
	}
	// ========================================================================
	
	public boolean isRunning() {
		// 
		
		aliveCheckc++;
		
		if ((nlpFactory!=null) && (nlpFactory.browser!=null)){
			isRunning = nlpFactory.browser.isActive();
			aliveCheckc = 0;
		}else{
			if (aliveCheckc<5){
				isRunning = true;
			}else{
				isRunning = false;
			}
		}
			
		return isRunning ;
	}


	
	protected void setNodeCount(int nodecount) {
		
		if (nodecount>25){
			nodeCount = nodecount;
		}
	}


	

	protected void startInstance() {
		
		appSecret = app.arguments.getAppSecret() ;
		
		instanceThrd.start();
	}
	
	@Override
	public void close(int exitcode) {
		
		nlpFactory.close();
		
		super.close(2) ;
	}

	protected void resume() {
		// 
		
		resumeMode = checkForPossibleResuming();
		instanceThrd.start();
	}


	private boolean checkForPossibleResuming() {
		
		return false;
	}
	
	protected boolean checkForConfiguration() {
		// 
		return true;
	}
	
	




	protected void prepareInstance() {

			
		
		// this might be called with some URL or xml-string that represents the source containing the settings
		// if this exists, it will be loaded
		
		brProperties = BrousProperties.getInstance(); 
		
		
		brProperties.setApplicationContext(contextMode);
		
		
		// dir = fileorg.getObjectStoreDir(pathitx)
		
		if (DFutils.folderExists(brProperties.getSystemRootDir())==false){
			brProperties.setSystemRootDir( IniProperties.fluidSomProjectBasePath );
		}

		NNBStartup.setLastProjectName(projectname); 
		NNBStartup.setProjectBasePath(homedir);
		NNBStartup.setLastProjectName(projectname); 
		NNBStartup.setProjectBasePath(homedir);

		brProperties.getPersistenceSettings().setProjectName(projectname);
		brProperties.getPersistenceSettings().setAppNameShortStr("nlp");
		brProperties.getPersistenceSettings().setPathToSomFluidSystemRootDir( brProperties.getSystemRootDir() );
		brProperties.getFileOrganizer().setRootDir( brProperties.getSystemRootDir() );
		brProperties.getFileOrganizer().setProjectDirName(DFutils.createPath(homedir, projectname)) ;
		brProperties.getFileOrganizer().setProjectBaseDir(homedir);
		
		brProperties.setApplicationHomeDir(this.homedir);
		
		brProperties.setActivateForSearchTerms(true) ;
		
		/*
		org.NooLab.somfluid.app.up.IniProperties = new IniProperties( IniProperties.fluidSomProjectBasePath,
				   													  IniProperties.lastProjectName,
				   													  IniProperties.dataSource);
											 // _projectBasePath,_lastProjectName, _dataSource
	    */
		
		explicitlySettingProperties();
		
		brProperties.setActivateForSearchTerms(true) ;
		
		brProperties.setOnCompletion( onCompletion );

		nlpFactory = BrowserFactory.get(brProperties);					   // creating the factory; calling without "factorymode" parameter, optimizer will be assumed	
		 
		nlpFactory.setTcpBox( getTcpBox() );
		
		nlpFactory.saveStartupTrace( nlpFlavor,  
									 brProperties.getStartupTraceInfo() );
		brProperties.save();
		
		
					int oport = app.arguments.getOutPort();
					int iport = app.arguments.getInPort();
		
		String str;
		
		str = brProperties.getApplicationContext() ;
		
		try {
			
			nlpFactory.go( );

							
		} catch (Exception e) {
			e.printStackTrace();
		}          		
		
		str = str + " " ;
	}
 
	


	private void explicitlySettingProperties(){
		
		 
		PersistenceSettings ps;
		 

		String str, dbDefinitionResource="" ;
		
		try {
			
			
			String instance = "ling";
			
			brProperties.setFlavor( "") ; // describes on language and functinoality

			// -------------------------------------------------------------------------------
			
			// "file", "db", "tcp"
			brProperties.setDataSourcing( DataStreamProviderIntf._DSP_SOURCED_TCP ,0) ;
			// brPropertiesDefaults.setDataSourcing( DataStreamProviderIntf._DSP_SOURCED_FILE ,0) ;
			
			brProperties.setDataSourceName( IniProperties.dataSource );
			
			
			
			 
			// -------------------------------------------------------------------------------
			
			/*
			 * 
			 
			// optional, if there are indeed plugin files ...
			// dependent on appcontext
			if (brProperties.isITexxContext()){
				
											out.print(2, "context detected : itexx");				
				str = brProperties.getSystemRootDir();
											out.print(4, "system root (1): "+str);				
				if (str.length()==0){
					
					brProperties.setSystemRootDir( IniProperties.fluidSomProjectBasePath) ;
					str = brProperties.getSystemRootDir();    // D:/data/iTexx/  ... it is empty when started by iTexx
											out.print(4, "system root (2): "+str);				
					if (str.length()==0){
						str = DFutils.getParentDir(homedir, 2);
						brProperties.setSystemRootDir( str) ; // D:/data/iTexx/app/ling
						str = brProperties.getSystemRootDir();
											out.print(4, "system root (3): "+str);				
					}
											out.print(4, "system root (4): "+str);						
				}
				 
				// later, we may allows plug-ins
				str = DFutils.createPath( brProperties.getSystemRootDir(), "app/ling/plugins/") ;
						out.print(4, "system root + partial algo path : "+str);				
				brProperties.setAlgorithmsConfigPath( str );
			}else{
				brProperties.setAlgorithmsConfigPath( "D:/dev/java/somfluid/plugins/" );
			}
			
			*/
			 
			str = brProperties.getSystemRootDir() ;
			str = IniProperties.fluidSomProjectBasePath;
			
			if (brProperties.isITexxContext()==false){
				brProperties.setSystemRootDir(str) ;
			}
			
			
			brProperties.setMultiProcessingLevel(0); 
			
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("The system will exit.");
			System.exit(-7) ;
		}
		
		// --- --- ---

		/*
		 * in this folder there will be enumerated sub-folders containing the results
		 */
		String resultFolder ;


		// set ready flag
		brProperties.setInitializationOK(true) ;						
	}
	 

	@Override
	protected void resumeInstance() throws Exception {
		
		System.out.println("app is now running, main thread waiting for <close> signal");
		
		while (nlpFactory.isClosing()==false){ // from the abstract class
			PrintLog.Delay(100);
		}
	}



	
}
